package handler

import (
    "fcs-api/src/app/business"
    "fcs-api/src/app/model"
    "net/http"
    "strconv"
    "strings"

    "fcs-api/src/app/utils"

    "context"
    i18n "git.inspur.com/x/common/i18n"
    "github.com/emicklei/go-restful"
    "github.com/minio/minio-go/v7"
)

var (
    Ak string
    Sk string
)

func (api *APIHandler) handleDownloadUserCode(request *restful.Request, response *restful.Response) {
    logger.Debug("handleDownloadUserCode start.")
    bucketName := model.Base.OssBucket
    token := utils.GetAdminToken()
    // 通过 groupId 和 functionId 查询对应的 groupName 和 functionName
    groupId := request.PathParameter("groupId")
    functionId := request.PathParameter("functionId")
    versionName := request.PathParameter("versionName")
    groupInfo := getGroupById(groupId).(*model.Group)
    functionInfo := getFunctionById(functionId).(*model.FuncInfo)
    codeInfo := &model.CodeInfo{
        GroupName:    groupInfo.Name,
        FunctionName: functionInfo.FunctionName,
        VersionName:  versionName,
    }
    user := currentUser(request)
    objectKey := user.AccountId + "/" + codeInfo.GroupName + "/" + codeInfo.FunctionName + "/" + codeInfo.VersionName + "/" + "code.zip"
    downloadLink, err := business.GetDownloadLink(model.Base.OssDownloadHost, bucketName, objectKey, "bearer "+token)
    if err != nil {
        handleInternalError(response, model.InternalServerError(model.TriggerError, i18n.GetMessage(request.Request, "fcs.code.get")))
        return
    }
    codeInfo.DownloadLink = downloadLink
    _ = response.WriteHeaderAndEntity(http.StatusOK, codeInfo)
}

func (api *APIHandler) handleDownloadCode(request *restful.Request, response *restful.Response) {
    // 这个函数只负责获取不受权限控制的公共代码的下载链接
    logger.Debug("handleDownloadCode start.")
    t := request.QueryParameter("name")
    l := request.QueryParameter("runtime")
    if t == "" {
        handleInternalError(response, model.InternalServerError(model.TriggerError, i18n.GetMessage(request.Request, "fcs.code.get")))
        return
    }
    bucketName := model.Base.OssBucket
    token := utils.GetAdminToken()
    zipName := t + "-" + strings.Replace(l, ".", "", -1) + ".zip"
    objectKey := "runtime" + "/" + l + "/" + zipName
    downloadLink, err := business.GetDownloadLink(model.Base.OssDownloadHost, bucketName, objectKey, "bearer "+token)
    if err != nil {
        handleInternalError(response, model.InternalServerError(model.TriggerError, i18n.GetMessage(request.Request, "fcs.code.get")))
        return
    }
    _ = response.WriteHeaderAndEntity(http.StatusOK, model.CodeInfo{DownloadLink: downloadLink})
    return
}

func (api *APIHandler) handleUploadCode(request *restful.Request, response *restful.Response) {

    logger.Debug("handleUploadCode start.")
    codeInfo := &model.CodeInfo{}
    err := ReadAndValidateEntity(request, codeInfo)
    if err != nil {
        _ = logger.Error("handleUploadCode ReadAndValidateEntity error %v", err)
        handleInternalError(response, model.InternalServerError(model.TriggerError, i18n.GetMessage(request.Request, "fcs.code.upload")))
        return
    }
    user := currentUser(request)
    uploadInfo := model.UploadInfo{
        Code:         codeInfo.Code,
        SourceBucket: codeInfo.Bucket,
        TargetBucket: model.Base.OssBucket,
        SourceObject: codeInfo.Object,
        TargetObject: "code.zip",
        TargetPath:   user.AccountId + "/" + codeInfo.GroupName + "/" + codeInfo.FunctionName + "/" + codeInfo.VersionName,
        Ak:           Ak,
        Sk:           Sk,
    }
    err = business.UploadCode(uploadInfo, request.HeaderParameter("Authorization"))
    if err != nil {
        handleInternalError(response, model.InternalServerError(model.TriggerError, "上传函数代码失败"))
        return
    }
    response.WriteHeader(http.StatusOK)
}

func (api *APIHandler) handleUploadLayer(request *restful.Request, response *restful.Response) {

    logger.Debug("handleUploadCode start.")
    layer := &model.LayerFileInfo{}
    rootId := request.PathParameter("layerId")
    err := ReadAndValidateEntity(request, layer)
    if err != nil {
        _ = logger.Error("handleUploadCode ReadAndValidateEntity error %v", err)
        handleInternalError(response, model.InternalServerError(model.TriggerError, "上传层失败"))
        return
    }
    user := currentUser(request)
    layerCount, err := model.GetLayerCountAll(rootId)
    if err != nil {
        _ = logger.Error("handleUploadLayer get layer count error %v", err)
        handleInternalError(response, model.InternalServerError(model.TriggerError, "上传层失败"))
        return
    }
    uploadInfo := model.UploadInfo{
        Code:         layer.Code,
        SourceBucket: layer.Bucket,
        TargetBucket: model.Base.OssBucket,
        SourceObject: layer.Object,
        TargetObject: "layer.zip",
        TargetPath:   user.Id + "-layer" + "/" + rootId + "/" + strconv.FormatInt(layerCount, 10),
        Ak:           Ak,
        Sk:           Sk,
    }
    err = business.UploadCode(uploadInfo, request.HeaderParameter("Authorization"))
    if err != nil {
        handleInternalError(response, model.InternalServerError(model.TriggerError, "上传函数代码失败"))
        return
    }
    response.WriteHeader(http.StatusOK)
}

func (api *APIHandler) handleDownloadLayer(request *restful.Request, response *restful.Response) {
    logger.Debug("handleDownloadUserCode start.")
    bucketName := model.Base.OssBucket
    token := utils.GetAdminToken()
    layerId := request.PathParameter("layerId")
    versionName := request.PathParameter("versionName")
    user := currentUser(request)
    // 和上传代码时候的路径规则对应
    objectKey := user.Id + "-layer" + "/" + layerId + "/" + versionName + "/" + "layer.zip"
    downloadLink, err := business.GetDownloadLink(model.Base.OssDownloadHost, bucketName, objectKey, "bearer "+token)
    if err != nil {
        handleInternalError(response, model.InternalServerError(model.TriggerError, "获取函数代码失败"))
        return
    }
    _ = response.WriteHeaderAndEntity(http.StatusOK, model.LayerFileInfo{DownloadLink: downloadLink})
}

func deleteCode(a5tId, groupName, funcName string) {
    bucketName := model.Base.OssBucket
    objectKey := a5tId + "/" + groupName + "/"
    if funcName != "" {
        objectKey = objectKey + funcName + "/"
    }

    err := model.Base.MinioClient.RemoveObject(context.Background(), bucketName, objectKey, minio.RemoveObjectOptions{ForceDelete: true})
    if err != nil {
        logger.Error("deleteCode err: %v", err)
    }
    //delUrl := model.Base.OssAdminHost + "oss/v1/buckets/" + bucketName + "/objects/" + objectKey
    //token := utils.GetAdminToken()
    //_, _ = utils.HttpReq(delUrl, "bearer "+token, http.MethodDelete, nil, nil)
}

func ossCopy(a5tId, groupName, funcName, sourceVersion, targetVersion string) error {

    bucketName := model.Base.OssBucket
    sourceCodeObj := a5tId + "/" + groupName + "/" + funcName + "/" + sourceVersion + "/" + "code.zip"
    sourceChecksumObj := a5tId + "/" + groupName + "/" + funcName + "/" + sourceVersion + "/" + "checksum"
    targetCodeObj := a5tId + "/" + groupName + "/" + funcName + "/" + targetVersion + "/" + "code.zip"
    targetChecksumObj := a5tId + "/" + groupName + "/" + funcName + "/" + targetVersion + "/" + "checksum"
    //codeSignedStr := utils.GetSignedStr(bucketName, targetCode, "", date, Ak, Sk, map[string]string{"x-oss-copy-source": sourceCode})
    //checkSignedStr := utils.GetSignedStr(bucketName, targetChecksum, "", date, Ak, Sk, map[string]string{"x-oss-copy-source": sourceChecksum})
    //codeUrl := model.Base.OssEndpoint + "/" + bucketName + "/" + targetCode
    //checkUrl := model.Base.OssEndpoint + "/" + bucketName + "/" + targetChecksum

    //复制代码
    _, err := model.Base.MinioClient.CopyObject(context.Background(),
        minio.CopyDestOptions{Bucket: bucketName, Object: targetCodeObj},
        minio.CopySrcOptions{Bucket: bucketName, Object: sourceCodeObj})
    if err != nil {
        return err
    }
    //err := business.OssUploadOrCopy(codeUrl, date, codeSignedStr, nil, map[string]string{"x-oss-copy-source": sourceCode})
    //if err != nil {
    //	return err
    //}

    //复制校验和
    _, err = model.Base.MinioClient.CopyObject(context.Background(),
        minio.CopyDestOptions{Bucket: bucketName, Object: targetChecksumObj},
        minio.CopySrcOptions{Bucket: bucketName, Object: sourceChecksumObj})
    //err = business.OssUploadOrCopy(checkUrl, date, checkSignedStr, nil, map[string]string{"x-oss-copy-source": sourceChecksum})
    if err != nil {
        return err
    }
    return nil
}

func functionOssCopy(a5tId, sGroupName, sFuncName, tGroupName, tFunctionName string) error {

    bucketName := model.Base.OssBucket
    version := model.FunctionLatestVersion
    sourceCode := a5tId + "/" + sGroupName + "/" + sFuncName + "/" + version + "/" + "code.zip"
    sourceChecksum := a5tId + "/" + sGroupName + "/" + sFuncName + "/" + version + "/" + "checksum"
    targetCode := a5tId + "/" + tGroupName + "/" + tFunctionName + "/" + version + "/" + "code.zip"
    targetChecksum := a5tId + "/" + tGroupName + "/" + tFunctionName + "/" + version + "/" + "checksum"
    //codeSignedStr := utils.GetSignedStr(bucketName, targetCode, "", date, Ak, Sk, map[string]string{"x-oss-copy-source": sourceCode})
    //checkSignedStr := utils.GetSignedStr(bucketName, targetChecksum, "", date, Ak, Sk, map[string]string{"x-oss-copy-source": sourceChecksum})
    //codeUrl := model.Base.OssEndpoint + "/" + bucketName + "/" + targetCode
    //checkUrl := model.Base.OssEndpoint + "/" + bucketName + "/" + targetChecksum

    //复制代码
    _, err := model.Base.MinioClient.CopyObject(context.Background(),
        minio.CopyDestOptions{Bucket: bucketName, Object: targetCode},
        minio.CopySrcOptions{Bucket: bucketName, Object: sourceCode})
    //err := business.OssUploadOrCopy(codeUrl, date, codeSignedStr, nil, map[string]string{"x-oss-copy-source": sourceCode})
    if err != nil {
        return err
    }

    //复制校验和
    _, err = model.Base.MinioClient.CopyObject(context.Background(),
        minio.CopyDestOptions{Bucket: bucketName, Object: targetChecksum},
        minio.CopySrcOptions{Bucket: bucketName, Object: sourceChecksum})
    if err != nil {
        return err
    }
    return nil
}
